﻿using System;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using StyleMVVM.Data.Activation;
using StyleMVVM.DependencyInjection;
using StyleMVVM.DependencyInjection.Impl;
using StyleMVVM.DotNet.Aspects.Impl;
using StyleMVVM.DotNet.Aspects.UnitTests.Classes;

namespace StyleMVVM.DotNet.Aspects.UnitTests.Impl
{
	/// <summary>
	/// Summary description for AspectClassCreatorTests
	/// </summary>
	[TestClass]
	public class AspectClassCreatorTests
	{
		private TestContext testContextInstance;

		/// <summary>
		///Gets or sets the test context which provides
		///information about and functionality for the current test run.
		///</summary>
		public TestContext TestContext
		{
			get
			{
				return testContextInstance;
			}
			set
			{
				testContextInstance = value;
			}
		}

		[TestMethod]
		public void SimpleMethodTest()
		{
			var newType = CreateNewAssembly(CreateBasicObjectAspectClassInfo());

			BasicObject newObject = Activator.CreateInstance(newType) as BasicObject;

			ICustomInitialization customInitialization = newObject as ICustomInitialization;

			Assert.IsNotNull(customInitialization);

			customInitialization.CustomInitialization(null, null);

			SimpleAspect.InvokeCount = 0;

			newObject.SimpleMethod();

			Assert.AreEqual(1, SimpleAspect.InvokeCount);
		}

		[TestMethod]
		public void SlightyMoreComplex()
		{
			var newType = CreateNewAssembly(CreateBasicObjectAspectClassInfo());

			BasicObject newObject = Activator.CreateInstance(newType) as BasicObject;

			ICustomInitialization customInitialization = newObject as ICustomInitialization;

			Assert.IsNotNull(customInitialization);

			customInitialization.CustomInitialization(null, null);

			SimpleAspect.InvokeCount = 0;

			int returnValue = newObject.SlightlyComplex("blah");

			Assert.AreEqual(10, returnValue);

			Assert.AreEqual(1, SimpleAspect.InvokeCount);
		}

		[TestMethod]
		public void LocateAspectTest()
		{
			DependencyInjectionContainer container = new DependencyInjectionContainer();

			container.RegisterAssembly(container.GetType().Assembly);

			container.Register<TestService>().As<ITestService>().AndSharedPermenantly();

			container.Start();

			var newType = CreateNewAssembly(CreateBasicObjectWithLocatedAspect());

			BasicObject newObject = Activator.CreateInstance(newType) as BasicObject;

			ICustomInitialization customInitialization = newObject as ICustomInitialization;

			Assert.IsNotNull(customInitialization);

			customInitialization.CustomInitialization(container, container.CreateInjectionContext());

			LocatedAspect.TestString = null;

			newObject.SimpleMethod();

			Assert.AreEqual("TestString", LocatedAspect.TestString);
		}

		[TestMethod]
		public void ReallyComplexTest()
		{
			DependencyInjectionContainer container = new DependencyInjectionContainer();

			container.RegisterAssembly(container.GetType().Assembly);

			container.Register<TestService>().As<ITestService>().AndSharedPermenantly();

			container.Start();

			var newType = CreateNewAssembly(CreateReallyComplex());

			BasicObject newObject = Activator.CreateInstance(newType) as BasicObject;

			ICustomInitialization customInitialization = newObject as ICustomInitialization;

			Assert.IsNotNull(customInitialization);

			customInitialization.CustomInitialization(container, container.CreateInjectionContext());

			SimpleAspect.InvokeCount = 0;

			int oValue;
			string oString;
			int iValue = 9;

			int returnValue = newObject.ReallyComplex("Hello", 10, 4, 6.5, 2, "hug", out oValue, out oString, ref iValue, 'i');

			Assert.AreEqual(1, SimpleAspect.InvokeCount);
			Assert.AreEqual(BasicObject.OVALUE, oValue);
			Assert.AreEqual(BasicObject.HMMMMM, oString);
			Assert.AreEqual(BasicObject.IVALUE, iValue);
			Assert.AreEqual(BasicObject.RETURN_VALUE, returnValue);
		}

		//[TestMethod]
		public void GenericMethodTest()
		{
			DependencyInjectionContainer container = new DependencyInjectionContainer();

			container.RegisterAssembly(container.GetType().Assembly);

			container.Register<TestService>().As<ITestService>().AndSharedPermenantly();

			container.Start();

			var newType = CreateNewAssembly(CreateGeneric());

			GenericMethodObject newObject = Activator.CreateInstance(newType) as GenericMethodObject;

			ICustomInitialization customInitialization = newObject as ICustomInitialization;

			Assert.IsNotNull(customInitialization);

			customInitialization.CustomInitialization(container, container.CreateInjectionContext());

			SimpleAspect.InvokeCount = 0;

			string fetchValue = newObject.Fetch(10);

			Assert.AreEqual(fetchValue, "10");
			Assert.AreEqual(1,SimpleAspect.InvokeCount);
		}

		private Type CreateNewAssembly(AspectClassInfo aspectClassInfo)
		{
			AppDomain myCurrentDomain = AppDomain.CurrentDomain;
			AssemblyName myAssemblyName = new AssemblyName();
			myAssemblyName.Name = "TempAssembly.dll";

			AssemblyBuilder myAssemblyBuilder = myCurrentDomain.DefineDynamicAssembly
				(myAssemblyName, AssemblyBuilderAccess.RunAndSave);

			// Define a dynamic module in this assembly.
			ModuleBuilder myModuleBuilder = myAssemblyBuilder.
				DefineDynamicModule("TempModule", "TempAssembly.dll");

			AspectClassCreator creator = new AspectClassCreator(myModuleBuilder, aspectClassInfo);

			Type newType = creator.CreateType();

			myAssemblyBuilder.Save("TempAssembly.dll");

			return newType;
		}

		private AspectClassInfo CreateBasicObjectWithLocatedAspect()
		{
			AspectClassInfo newInfo = new AspectClassInfo { ClassType = typeof(BasicObject), ClassName = typeof(BasicObject).FullName + "_Proxy" };

			newInfo.Aspects = new List<AspectInfo>
				                  {
											//new AspectInfo
											//	{
											//		AspectType = typeof(SimpleAspect),
											//		MemberInfo = typeof(BasicObject).GetProperty("TestProp")
											//	},

											new AspectInfo
						                  {
							                  AspectType = typeof(LocatedAspect),
													MemberInfo = typeof(BasicObject).GetMethod("SimpleMethod")
						                  },

											new AspectInfo
												{
													AspectType = typeof(LocatedAspect),
													MemberInfo = typeof(BasicObject).GetMethod("SlightlyComplex")
												}
				                  };

			return newInfo;
		}

		private AspectClassInfo CreateBasicObjectAspectClassInfo()
		{
			AspectClassInfo newInfo = new AspectClassInfo { ClassType = typeof(BasicObject), ClassName = typeof(BasicObject).FullName + "_Proxy" };

			newInfo.Aspects = new List<AspectInfo>
				                  {
											//new AspectInfo
											//	{
											//		AspectType = typeof(SimpleAspect),
											//		MemberInfo = typeof(BasicObject).GetProperty("TestProp")
											//	},

											new AspectInfo
						                  {
							                  AspectType = typeof(SimpleAspect),
													MemberInfo = typeof(BasicObject).GetMethod("SimpleMethod")
						                  },

											new AspectInfo
												{
													AspectType = typeof(SimpleAspect),
													MemberInfo = typeof(BasicObject).GetMethod("SlightlyComplex")
												}
				                  };

			return newInfo;
		}

		private AspectClassInfo CreateReallyComplex()
		{
			AspectClassInfo newInfo = new AspectClassInfo { ClassType = typeof(BasicObject), ClassName = typeof(BasicObject).FullName + "_Proxy" };

			newInfo.Aspects = new List<AspectInfo>
				                  {
											new AspectInfo
												{
													AspectType = typeof(SimpleAspect),
													MemberInfo = typeof(BasicObject).GetMethod("ReallyComplex")
												}
				                  };

			return newInfo;
		}

		private AspectClassInfo CreateGeneric()
		{
			AspectClassInfo newInfo = new AspectClassInfo
												  {
													  ClassType = typeof(GenericMethodObject),
													  ClassName = typeof(GenericMethodObject).FullName + "_Proxy"
												  };

			newInfo.Aspects = new List<AspectInfo>
				                  {
											new AspectInfo
												{
													AspectType = typeof(SimpleAspect),
													MemberInfo = typeof(GenericMethodObject).GetMethod("Fetch")
												}
				                  };

			return newInfo;
		}
	}
}
